home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / MDB / ibase.php < prev    next >
PHP Script  |  2004-03-24  |  68KB  |  1,828 lines

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP Version 4                                                        |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
  6. // | Stig. S. Bakken, Lukas Smith                                         |
  7. // | All rights reserved.                                                 |
  8. // +----------------------------------------------------------------------+
  9. // | MDB is a merge of PEAR DB and Metabases that provides a unified DB   |
  10. // | API as well as database abstraction for PHP applications.            |
  11. // | This LICENSE is in the BSD license style.                            |
  12. // |                                                                      |
  13. // | Redistribution and use in source and binary forms, with or without   |
  14. // | modification, are permitted provided that the following conditions   |
  15. // | are met:                                                             |
  16. // |                                                                      |
  17. // | Redistributions of source code must retain the above copyright       |
  18. // | notice, this list of conditions and the following disclaimer.        |
  19. // |                                                                      |
  20. // | Redistributions in binary form must reproduce the above copyright    |
  21. // | notice, this list of conditions and the following disclaimer in the  |
  22. // | documentation and/or other materials provided with the distribution. |
  23. // |                                                                      |
  24. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
  25. // | Lukas Smith nor the names of his contributors may be used to endorse |
  26. // | or promote products derived from this software without specific prior|
  27. // | written permission.                                                  |
  28. // |                                                                      |
  29. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  30. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  31. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  32. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  33. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  34. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  35. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  36. // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
  37. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  38. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  39. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  40. // | POSSIBILITY OF SUCH DAMAGE.                                          |
  41. // +----------------------------------------------------------------------+
  42. // | Author: Lorenzo Alberton <l.alberton@quipo.it>                       |
  43. // +----------------------------------------------------------------------+
  44. //
  45. // $Id: ibase.php,v 1.9.4.15 2004/01/08 13:43:02 lsmith Exp $
  46.  
  47. require_once 'MDB/Common.php';
  48.  
  49. /**
  50.  * MDB FireBird/InterBase driver
  51.  *
  52.  * Notes:
  53.  * - when fetching in associative mode all keys are lowercased.
  54.  *
  55.  * - Currently, the driver relies on the Interbase server to use SQL dialect 3
  56.  *   that was introduced with Interbase 6. Some versions of Interbase server,
  57.  *   like the Super Server, do not seem to work by default with dialect 3.
  58.  *   This may lead to errors when trying to create tables using Interbase SQL
  59.  *   data types that are only available when using this dialect version.
  60.  *
  61.  * - Interbase does not support per field index sorting support. Indexes are
  62.  *   either ascending, descending or both even when they are defined from more
  63.  *   than one field. Currently Metabase Interbase driver uses the index sorting
  64.  *   type given by the first field of the index for which it is specified the
  65.  *   sorting type.
  66.  *
  67.  * - The numRows method is emulated by fetching all the rows into memory.
  68.  *   Avoid using it if for queries with large result sets.
  69.  *
  70.  * - Interbase does not provide direct support for returning result sets
  71.      restrictedto a given range. Such support is emulated in the MDB ibase driver.
  72.  *
  73.  * - Current Interbase versions do not support altering table field DEFAULT
  74.  *   values and NOT NULL constraint. Text fields' length may only be raised in
  75.  *   increments defined by Interbase, so the Metabase Interbase does not support
  76.  *   altering text field length yet.
  77.  *
  78.  * - createDatabase and dropDatabase are not supported
  79.  *
  80.  * - MDB creates Interbase blobs before executing a prepared queries to insert
  81.  *   or update large object fields. If such queries fail to execute, MDB
  82.  *   Interbase driver class is not able to reclaim the database space allocated
  83.  *   for the large object values because there is currently no PHP function to do so.
  84.  *
  85.  * @package MDB
  86.  * @category Database
  87.  * @author  Lorenzo Alberton <l.alberton@quipo.it>
  88.  */
  89.  
  90. class MDB_ibase extends MDB_Common
  91. {
  92.     var $connection = 0;
  93.     var $connected_host;
  94.     var $connected_port;
  95.     var $selected_database = '';
  96.     var $selected_database_file = '';
  97.     var $opened_persistent = '';
  98.     var $transaction_id = 0;
  99.  
  100.     var $escape_quotes = "'";
  101.     var $decimal_factor = 1.0;
  102.  
  103.     var $results = array();
  104.     var $current_row = array();
  105.     var $columns = array();
  106.     var $rows = array();
  107.     var $limits = array();
  108.     var $row_buffer = array();
  109.     var $highest_fetched_row = array();
  110.     var $query_parameters = array();
  111.     var $query_parameter_values = array();
  112.  
  113.     // }}}
  114.     // {{{ constructor
  115.  
  116.     /**
  117.     * Constructor
  118.     */
  119.     function MDB_ibase()
  120.     {
  121.         $this->MDB_Common();
  122.         $this->phptype  = 'ibase';
  123.         $this->dbsyntax = 'ibase';
  124.  
  125.         $this->supported['Sequences'] = 1;
  126.         $this->supported['Indexes'] = 1;
  127.         $this->supported['SummaryFunctions'] = 1;
  128.         $this->supported['OrderByText'] = 1;
  129.         $this->supported['Transactions'] = 1;
  130.         $this->supported['CurrId'] = 1;
  131.         $this->supported['SelectRowRanges'] = 1;
  132.         $this->supported['LOBs'] = 1;
  133.         $this->supported['Replace'] = 1;
  134.         $this->supported['SubSelects'] = 1;
  135.  
  136.         $this->decimal_factor = pow(10.0, $this->decimal_places);
  137.  
  138.         $this->options['DatabasePath'] = '';
  139.         $this->options['DatabaseExtension'] = '.gdb';
  140.         $this->options['DBAUser'] = FALSE;
  141.         $this->options['DBAPassword'] = FALSE;
  142.  
  143.         $this->errorcode_map = array(
  144.             -104 => MDB_ERROR_SYNTAX,
  145.             -150 => MDB_ERROR_ACCESS_VIOLATION,
  146.             -151 => MDB_ERROR_ACCESS_VIOLATION,
  147.             -155 => MDB_ERROR_NOSUCHTABLE,
  148.               88 => MDB_ERROR_NOSUCHTABLE,
  149.             -157 => MDB_ERROR_NOSUCHFIELD,
  150.             -158 => MDB_ERROR_VALUE_COUNT_ON_ROW,
  151.             -170 => MDB_ERROR_MISMATCH,
  152.             -171 => MDB_ERROR_MISMATCH,
  153.             -172 => MDB_ERROR_INVALID,
  154.             -204 => MDB_ERROR_INVALID,
  155.             -205 => MDB_ERROR_NOSUCHFIELD,
  156.             -206 => MDB_ERROR_NOSUCHFIELD,
  157.             -208 => MDB_ERROR_INVALID,
  158.             -219 => MDB_ERROR_NOSUCHTABLE,
  159.             -297 => MDB_ERROR_CONSTRAINT,
  160.             -530 => MDB_ERROR_CONSTRAINT,
  161.             -607 => MDB_ERROR_NOSUCHTABLE,
  162.             -803 => MDB_ERROR_CONSTRAINT,
  163.             -551 => MDB_ERROR_ACCESS_VIOLATION,
  164.             -552 => MDB_ERROR_ACCESS_VIOLATION,
  165.             -922 => MDB_ERROR_NOSUCHDB,
  166.             -923 => MDB_ERROR_CONNECT_FAILED,
  167.             -924 => MDB_ERROR_CONNECT_FAILED
  168.         );
  169.  
  170.     }
  171.  
  172.     // }}}
  173.     // {{{ errorCode()
  174.  
  175.     /**
  176.      * Map native error codes to DB's portable ones.  Requires that
  177.      * the DB implementation's constructor fills in the $errorcode_map
  178.      * property.
  179.      *
  180.      * @param $nativecode the native error code, as returned by the backend
  181.      * database extension (string or integer)
  182.      * @return int a portable MDB error code, or FALSE if this DB
  183.      * implementation has no mapping for the given error code.
  184.      */
  185.     function errorCode($errormsg)
  186.     {
  187.         static $error_regexps;
  188.         if (empty($error_regexps)) {
  189.             $error_regexps = array(
  190.                 '/(Table does not exist\.|Relation [\"\'].*[\"\'] does not exist|sequence does not exist|class ".+" not found)$/' => MDB_ERROR_NOSUCHTABLE,
  191.                 '/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*/'      => MDB_ERROR_ALREADY_EXISTS,
  192.                 '/divide by zero$/'                     => MDB_ERROR_DIVZERO,
  193.                 '/pg_atoi: error in .*: can\'t parse /' => MDB_ERROR_INVALID_NUMBER,
  194.                 '/ttribute [\"\'].*[\"\'] not found$|Relation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => MDB_ERROR_NOSUCHFIELD,
  195.                 '/parser: parse error at or near \"/'   => MDB_ERROR_SYNTAX,
  196.                 '/referential integrity violation/'     => MDB_ERROR_CONSTRAINT
  197.             );
  198.         }
  199.         foreach ($error_regexps as $regexp => $code) {
  200.             if (preg_match($regexp, $errormsg)) {
  201.                 return $code;
  202.             }
  203.         }
  204.         // Fall back to MDB_ERROR if there was no mapping.
  205.         return MDB_ERROR;
  206.     }
  207.  
  208.     // }}}
  209.     // {{{ ibaseRaiseError()
  210.  
  211.     /**
  212.      * This method is used to communicate an error and invoke error
  213.      * callbacks etc.  Basically a wrapper for MDB::raiseError
  214.      * that checks for native error msgs.
  215.      *
  216.      * @param integer $db_errno error code
  217.      * @return object a PEAR error object
  218.      * @access public
  219.      * @see PEAR_Error
  220.      */
  221.  
  222.     function ibaseRaiseError($db_errno = NULL)
  223.     {
  224.         $native_errmsg = $this->errorNative();
  225.         // memo for the interbase php module hackers: we need something similar
  226.         // to mysql_errno() to retrieve error codes instead of this ugly hack
  227.         if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/', $native_errmsg, $m)) {
  228.             $native_errno = (int)$m[2];
  229.         } else {
  230.             $native_errno = NULL;
  231.         }
  232.         // try to map the native error to the DB one
  233.         if ($db_errno === NULL) {
  234.             if ($native_errno) {
  235.                 // try to interpret Interbase error code (that's why we need ibase_errno()
  236.                 // in the interbase module to return the real error code)
  237.                 switch ($native_errno) {
  238.                     case -204:
  239.                         if (is_int(strpos($m[3], 'Table unknown'))) {
  240.                             $db_errno = MDB_ERROR_NOSUCHTABLE;
  241.                         }
  242.                     break;
  243.                     default:
  244.                         $db_errno = $this->errorCode($native_errno);
  245.                 }
  246.             } else {
  247.                 $error_regexps = array(
  248.                     '/[tT]able .* already exists/' => MDB_ERROR_ALREADY_EXISTS,
  249.                     '/violation of FOREIGN KEY constraint/' => MDB_ERROR_CONSTRAINT,
  250.                     '/conversion error from string/' => MDB_ERROR_INVALID_NUMBER,
  251.                     '/arithmetic exception, numeric overflow, or string truncation/' => MDB_ERROR_DIVZERO
  252.                 );
  253.                 foreach ($error_regexps as $regexp => $code) {
  254.                     if (preg_match($regexp, $native_errmsg, $m)) {
  255.                         $db_errno = $code;
  256.                         $native_errno = NULL;
  257.                         break;
  258.                     }
  259.                 }
  260.             }
  261.         }
  262.         return $this->raiseError($db_errno, NULL, NULL, NULL, $native_errmsg);
  263.     }
  264.  
  265.     // }}}
  266.     // {{{ errorNative()
  267.  
  268.     /**
  269.      * Get the native error code of the last error (if any) that
  270.      * occured on the current connection.
  271.      *
  272.      * @access public
  273.      * @return int native ibase error code
  274.      */
  275.     function errorNative()
  276.     {
  277.         return ibase_errmsg();
  278.     }
  279.  
  280.     // }}}
  281.     // {{{ autoCommit()
  282.  
  283.     /**
  284.      * Define whether database changes done on the database be automatically
  285.      * committed. This function may also implicitly start or end a transaction.
  286.      *
  287.      * @param boolean $auto_commit flag that indicates whether the database
  288.      *     changes should be committed right after executing every query
  289.      *     statement. If this argument is 0 a transaction implicitly started.
  290.      *     Otherwise, if a transaction is in progress it is ended by committing
  291.      *     any database changes that were pending.
  292.      * @return mixed MDB_OK on success, a MDB error on failure
  293.      * @access public
  294.      */
  295.     function autoCommit($auto_commit)
  296.     {
  297.         $this->debug('AutoCommit: '.($auto_commit ? 'On' : 'Off'));
  298.         if ((!$this->auto_commit) == (!$auto_commit)) {
  299.             return MDB_OK;
  300.         }
  301.         if ($this->connection && $auto_commit && MDB::isError($commit = $this->commit())) {
  302.             return($commit);
  303.         }
  304.         $this->auto_commit = $auto_commit;
  305.         $this->in_transaction = !$auto_commit;
  306.         return MDB_OK;
  307.     }
  308.  
  309.     // }}}
  310.     // {{{ commit()
  311.  
  312.     /**
  313.      * Commit the database changes done during a transaction that is in
  314.      * progress. This function may only be called when auto-committing is
  315.      * disabled, otherwise it will fail. Therefore, a new transaction is
  316.      * implicitly started after committing the pending changes.
  317.      *
  318.      * @return mixed MDB_OK on success, a MDB error on failure
  319.      * @access public
  320.      */
  321.     function commit()
  322.     {
  323.         $this->debug('Commit Transaction');
  324.         if ($this->auto_commit) {
  325.             return($this->raiseError(MDB_ERROR, '', '', 'Commit: transaction changes are being auto commited'));
  326.         }
  327.         return @ibase_commit($this->connection);
  328.     }
  329.  
  330.     // }}}
  331.     // {{{ rollback()
  332.  
  333.     /**
  334.      * Cancel any database changes done during a transaction that is in
  335.      * progress. This function may only be called when auto-committing is
  336.      * disabled, otherwise it will fail. Therefore, a new transaction is
  337.      * implicitly started after canceling the pending changes.
  338.      *
  339.      * @return mixed MDB_OK on success, a MDB error on failure
  340.      * @access public
  341.      */
  342.     function rollback()
  343.     {
  344.         $this->debug('Rollback Transaction');
  345.         if ($this->auto_commit) {
  346.             return($this->raiseError(MDB_ERROR, '', '', 'Rollback: transactions can not be rolled back when changes are auto commited'));
  347.         }
  348.  
  349.         //return ibase_rollback($this->connection);
  350.  
  351.         if ($this->transaction_id && !ibase_rollback($this->connection)) {
  352.             return($this->raiseError(MDB_ERROR, '', '', 'Rollback: Could not rollback a pending transaction: '.ibase_errmsg()));
  353.         }
  354.         if (!$this->transaction_id = ibase_trans(IBASE_COMMITTED, $this->connection)) {
  355.             return($this->raiseError(MDB_ERROR, '', '', 'Rollback: Could not start a new transaction: '.ibase_errmsg()));
  356.         }
  357.         return MDB_OK;
  358.     }
  359.  
  360.     // }}}
  361.     // {{{ getDatabaseFile()
  362.  
  363.     function getDatabaseFile($database_name)
  364.     {
  365.         if (isset($this->options['DatabasePath'])) {
  366.             $this->database_path = $this->options['DatabasePath'];
  367.         }
  368.         if (isset($this->options['DatabaseExtension'])) {
  369.             $this->database_extension = $this->options['DatabaseExtension'];
  370.         }
  371.         //$this->database_path = (isset($this->options['DatabasePath']) ? $this->options['DatabasePath'] : '');
  372.         //$this->database_extension = (isset($this->options['DatabaseExtension']) ? $this->options['DatabaseExtension'] : '.gdb');
  373.  
  374.         //$database_path = (isset($this->options['DatabasePath']) ? $this->options['DatabasePath'] : '');
  375.         //$database_extension = (isset($this->options['DatabaseExtension']) ? $this->options['DatabaseExtension'] : '.gdb');
  376.         return $this->database_path.$database_name.$this->database_extension;
  377.     }
  378.  
  379.     // }}}
  380.     // {{{ _doConnect()
  381.  
  382.     /**
  383.      * Does the grunt work of connecting to the database
  384.      *
  385.      * @return mixed connection resource on success, MDB_Error on failure
  386.      * @access private
  387.      **/
  388.     function _doConnect($database_name, $persistent)
  389.     {
  390.         $function = ($persistent ? 'ibase_pconnect' : 'ibase_connect');
  391.         if (!function_exists($function)) {
  392.             return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'doConnect: FireBird/InterBase support is not available in this PHP configuration'));
  393.         }
  394.  
  395.         $dbhost = $this->host ?
  396.                   ($this->host . ':' . $database_name) :
  397.                   $database_name;
  398.  
  399.         $params = array();
  400.         $params[] = $dbhost;
  401.         $params[] = !empty($this->user) ? $this->user : NULL;
  402.         $params[] = !empty($this->password) ? $this->password : NULL;
  403.  
  404.         $connection = @call_user_func_array($function, $params);
  405.         if ($connection > 0) {
  406.             ibase_timefmt("%Y-%m-%d %H:%M:%S", IBASE_TIMESTAMP);
  407.             ibase_timefmt("%Y-%m-%d", IBASE_DATE);
  408.             return $connection;
  409.         }
  410.         if (isset($php_errormsg)) {
  411.             $error_msg = $php_errormsg;
  412.         } else {
  413.             $error_msg = 'Could not connect to FireBird/InterBase server';
  414.         }
  415.         return($this->raiseError(MDB_ERROR_CONNECT_FAILED, '', '', 'doConnect: '.$error_msg));
  416.     }
  417.  
  418.     // }}}
  419.     // {{{ connect()
  420.  
  421.     /**
  422.      * Connect to the database
  423.      *
  424.      * @return TRUE on success, MDB_Error on failure
  425.      * @access public
  426.      **/
  427.     function connect()
  428.     {
  429.         $port = (isset($this->options['port']) ? $this->options['port'] : '');
  430.  
  431.         $database_file = $this->getDatabaseFile($this->database_name);
  432.  
  433.         if ($this->connection != 0) {
  434.             if (!strcmp($this->connected_host, $this->host)
  435.                 && !strcmp($this->connected_port, $port)
  436.                 && !strcmp($this->selected_database_file, $database_file)
  437.                 && ($this->opened_persistent == $this->options['persistent']))
  438.             {
  439.                 return MDB_OK;
  440.             }
  441.             ibase_close($this->connection);
  442.             $this->affected_rows = -1;
  443.             $this->connection = 0;
  444.         }
  445.         $connection = $this->_doConnect($database_file, $this->options['persistent']);
  446.         if (MDB::isError($connection)) {
  447.             return $connection;
  448.         }
  449.         $this->connection = $connection;
  450.  
  451.         //the if below was added after PEAR::DB. Review me!!
  452.         if ($this->dbsyntax == 'fbird') {
  453.             $this->supported['limit'] = 'alter';
  454.         }
  455.  
  456.         if (!$this->auto_commit && MDB::isError($trans_result = $this->_doQuery('BEGIN'))) {
  457.             ibase_close($this->connection);
  458.             $this->connection = 0;
  459.             $this->affected_rows = -1;
  460.             return $trans_result;
  461.         }
  462.         $this->connected_host = $this->host;
  463.         $this->connected_port = $port;
  464.         $this->selected_database_file = $database_file;
  465.         $this->opened_persistent = $this->options['persistent'];
  466.         return MDB_OK;
  467.     }
  468.  
  469.     // }}}
  470.     // {{{ _close()
  471.     /**
  472.      * Close the database connection
  473.      *
  474.      * @return boolean
  475.      * @access private
  476.      **/
  477.     function _close()
  478.     {
  479.         if ($this->connection != 0) {
  480.             if (!$this->auto_commit) {
  481.                 $this->_doQuery('END');
  482.             }
  483.             ibase_close($this->connection);
  484.             $this->connection = 0;
  485.             $this->affected_rows = -1;
  486.  
  487.             unset($GLOBALS['_MDB_databases'][$this->database]);
  488.             return true;
  489.         }
  490.         return false;
  491.     }
  492.  
  493.     // }}}
  494.     // {{{ _doQuery()
  495.  
  496.     /**
  497.      * Execute a query
  498.      * @param string $query the SQL query
  499.      * @return mixed result identifier if query executed, else MDB_error
  500.      * @access private
  501.      **/
  502.     function _doQuery($query, $first=0, $limit=0, $prepared_query=0)  // function _doQuery($query)
  503.     {
  504.         $connection = ($this->auto_commit ? $this->connection : $this->transaction_id);
  505.         if ($prepared_query
  506.             && isset($this->query_parameters[$prepared_query])
  507.             && count($this->query_parameters[$prepared_query]) > 2)
  508.         {
  509.  
  510.             $this->query_parameters[$prepared_query][0] = $connection;
  511.             $this->query_parameters[$prepared_query][1] = $query;
  512.             $result = @call_user_func_array("ibase_query", $this->query_parameters[$prepared_query]);
  513.         } else {
  514.             //Not Prepared Query
  515.             $result = @ibase_query($connection, $query);
  516.             if (ibase_errmsg() == 'Query argument missed') { //ibase_errcode() only available in PHP5
  517.                 //connection lost, try again...
  518.                 $this->connect();
  519.                 $result = @ibase_query($this->connection, $query);
  520.             }
  521.         }
  522.         if ($result) {
  523.             if (!MDB::isManip($query)) {
  524.                 $result_value = intval($result);
  525.                 $this->current_row[$result_value] = -1;
  526.                 if ($limit > 0) {
  527.                     $this->limits[$result_value] = array($first, $limit, 0);
  528.                 }
  529.                 $this->highest_fetched_row[$result_value] = -1;
  530.             } else {
  531.                 $this->affected_rows = -1;
  532.             }
  533.         } else {
  534.             return($this->raiseError(MDB_ERROR, NULL, NULL, '_doQuery: Could not execute query ("'.$query.'"): ' . ibase_errmsg()));
  535.         }
  536.         return $result;
  537.     }
  538.  
  539.     // }}}
  540.     // {{{ query()
  541.  
  542.     /**
  543.      * Send a query to the database and return any results
  544.      *
  545.      * @param string $query the SQL query
  546.      * @param array $types array that contains the types of the columns in
  547.      *                         the result set
  548.      * @return mixed result identifier if query executed, else MDB_error
  549.      * @access public
  550.      **/
  551.     function query($query, $types = NULL)
  552.     {
  553.         $this->debug('Query: '.$query);
  554.         $this->last_query = $query;
  555.         $first = $this->first_selected_row;
  556.         $limit = $this->selected_row_limit;
  557.         $this->first_selected_row = $this->selected_row_limit = 0;
  558.         $connected = $this->connect();
  559.         if (MDB::isError($connected)) {
  560.             return $connected;
  561.         }
  562.  
  563.         if (!MDB::isError($result = $this->_doQuery($query, $first, $limit, 0))) {
  564.             if ($types != NULL) {
  565.                 if (!is_array($types)) {
  566.                     $types = array($types);
  567.                 }
  568.                 if (MDB::isError($err = $this->setResultTypes($result, $types))) {
  569.                     $this->freeResult($result);
  570.                     return $err;
  571.                 }
  572.             }
  573.             return $result;
  574.         }
  575.         return $this->ibaseRaiseError();
  576.  
  577.     }
  578.  
  579.     // }}}
  580.     // {{{ _executePreparedQuery()
  581.  
  582.     /**
  583.      * Execute a prepared query statement.
  584.      *
  585.      * @param int $prepared_query argument is a handle that was returned by
  586.      *       the function prepareQuery()
  587.      * @param string $query query to be executed
  588.      * @param array $types array that contains the types of the columns in
  589.      *       the result set
  590.      * @return mixed a result handle or MDB_OK on success, a MDB error on failure
  591.      * @access private
  592.      */
  593.     function _executePreparedQuery($prepared_query, $query)
  594.     {
  595.         $first = $this->first_selected_row;
  596.         $limit = $this->selected_row_limit;
  597.         $this->first_selected_row = $this->selected_row_limit = 0;
  598.         if (MDB::isError($connect = $this->connect())) {
  599.             return $connect;
  600.         }
  601.         return($this->_doQuery($query, $first, $limit, $prepared_query));
  602.     }
  603.  
  604.     // }}}
  605.     // {{{ _skipLimitOffset()
  606.  
  607.     /**
  608.      * Skip the first row of a result set.
  609.      *
  610.      * @param resource $result
  611.      * @return mixed a result handle or MDB_OK on success, a MDB error on failure
  612.      * @access private
  613.      */
  614.     function _skipLimitOffset($result)
  615.     {
  616.         $result_value = intval($result);
  617.         $first = $this->limits[$result_value][0];
  618.         for (; $this->limits[$result_value][2] < $first; $this->limits[$result_value][2]++) {
  619.             if (!is_array(@ibase_fetch_assoc($result))) {
  620.                 $this->limits[$result_value][2] = $first;
  621.                 return($this->raiseError(MDB_ERROR, NULL, NULL,
  622.                     'Skip first rows: could not skip a query result row'));
  623.             }
  624.         }
  625.         return MDB_OK;
  626.     }
  627.  
  628.     // }}}
  629.     // {{{ getColumnNames()
  630.  
  631.     /**
  632.      * Retrieve the names of columns returned by the DBMS in a query result.
  633.      *
  634.      * @param resource $result  result identifier
  635.      * @return mixed an associative array variable
  636.      *                               that will hold the names of columns.The
  637.      *                               indexes of the array are the column names
  638.      *                               mapped to lower case and the values are the
  639.      *                               respective numbers of the columns starting
  640.      *                               from 0. Some DBMS may not return any
  641.      *                               columns when the result set does not
  642.      *                               contain any rows.
  643.      *
  644.      *                               a MDB error on failure
  645.      * @access public
  646.      */
  647.     function getColumnNames($result)
  648.     {
  649.         $result_value = intval($result);
  650.         if (!isset($this->highest_fetched_row[$result_value])) {
  651.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  652.                 'Get column names: it was specified an inexisting result set'));
  653.         }
  654.         if (!isset($this->columns[$result_value])) {
  655.             $this->columns[$result_value] = array();
  656.             $columns = ibase_num_fields($result);
  657.             for ($column=0; $column < $columns; $column++) {
  658.                 $column_info = ibase_field_info($result, $column);
  659.                 $this->columns[$result_value][strtolower($column_info["name"])] = $column;
  660.             }
  661.         }
  662.         return $this->columns[$result_value];
  663.     }
  664.  
  665.     // }}}
  666.     // {{{ numCols()
  667.  
  668.     /**
  669.      * Count the number of columns returned by the DBMS in a query result.
  670.      *
  671.      * @param resource $result result identifier
  672.      * @return mixed integer value with the number of columns, a MDB error
  673.      *      on failure
  674.      * @access public
  675.      */
  676.     function numCols($result)
  677.     {
  678.         if (!isset($this->highest_fetched_row[intval($result)])) {
  679.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  680.                 'Number of columns: it was specified an inexisting result set'));
  681.         }
  682.         return ibase_num_fields($result);
  683.     }
  684.  
  685.     // }}}
  686.     // {{{ endOfResult()
  687.  
  688.     /**
  689.     * check if the end of the result set has been reached
  690.     *
  691.     * @param resource    $result result identifier
  692.     * @return mixed TRUE or FALSE on sucess, a MDB error on failure
  693.     * @access public
  694.     */
  695.     function endOfResult($result)
  696.     {
  697.         $result_value = intval($result);
  698.         if (!isset($this->current_row[$result_value])) {
  699.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  700.                 'End of result: attempted to check the end of an unknown result'));
  701.         }
  702.         if (isset($this->rows[$result_value])) {
  703.             return($this->highest_fetched_row[$result_value] >= $this->rows[$result_value]-1);
  704.         }
  705.         if (isset($this->row_buffer[$result_value])) {
  706.             return false;
  707.         }
  708.         if (isset($this->limits[$result_value])) {
  709.             if (MDB::isError($this->_skipLimitOffset($result))
  710.                 || $this->current_row[$result_value] + 1 >= $this->limits[$result_value][1]
  711.             ) {
  712.                 $this->rows[$result_value] = 0;
  713.                 return true;
  714.             }
  715.         }
  716.         if (is_array($this->row_buffer[$result_value] = @ibase_fetch_assoc($result))) {
  717.             return false;
  718.         }
  719.         unset($this->row_buffer[$result_value]);
  720.         return true;
  721.     }
  722.  
  723.     // }}}
  724.     // {{{ _getColumn()
  725.  
  726.     /**
  727.      * Get key for a given field with a result set.
  728.      *
  729.      * @param resource $result
  730.      * @param mixed $field integer or string key for the column
  731.      * @return mixed column from the result handle or a MDB error on failure
  732.      * @access private
  733.      */
  734.     function _getColumn($result, $field)
  735.     {
  736.         $result_value = intval($result);
  737.         if (MDB::isError($names = $this->getColumnNames($result))) {
  738.             return $names;
  739.         }
  740.         if (is_numeric($field)) {
  741.             if (($column = $field) < 0
  742.                 || $column >= count($this->columns[$result_value]))
  743.             {
  744.                 return($this->raiseError('Get column: attempted to fetch an query result column out of range'));
  745.             }
  746.         } else {
  747.             $name = strtolower($field);
  748.             if (!isset($this->columns[$result_value][$name])) {
  749.                 return($this->raiseError('Get column: attempted to fetch an unknown query result column'));
  750.             }
  751.             $column = $this->columns[$result_value][$name];
  752.         }
  753.         return $column;
  754.     }
  755.  
  756.     // }}}
  757.     // {{{ fetch()
  758.  
  759.     /**
  760.      * fetch value from a result set
  761.      *
  762.      * @param resource $result result identifier
  763.      * @param int $rownum number of the row where the data can be found
  764.      * @param int $field field number where the data can be found
  765.      * @return mixed string on success, a MDB error on failure
  766.      * @access public
  767.      */
  768.     function fetch($result, $rownum, $field)
  769.     {
  770.         if (MDB::isError($column = $this->_getColumn($result, $field))) {
  771.             return $column;
  772.         }
  773.         $row = $this->fetchInto($result, MDB_FETCHMODE_ORDERED, $rownum);
  774.         if (MDB::isError($row)) {
  775.             return $row;
  776.         }
  777.         if (!isset($row[$column])) {
  778.             return null;
  779.         }
  780.         return $row[$column];
  781.     }
  782.  
  783.     // }}}
  784.     // {{{ fetchInto()
  785.  
  786.     /**
  787.      * Fetch a row and return data in an array.
  788.      *
  789.      * @param resource $result result identifier
  790.      * @param int $fetchmode how the array data should be indexed
  791.      * @param int $rownum the row number to fetch
  792.      * @return mixed data array or NULL on success, a MDB error on failure
  793.      * @access public
  794.      */
  795.     function fetchInto($result, $fetchmode=MDB_FETCHMODE_DEFAULT, $rownum=null)
  796.     {
  797.         $result_value = intval($result);
  798.         if ($fetchmode == MDB_FETCHMODE_DEFAULT) {
  799.             $fetchmode = $this->fetchmode;
  800.         }
  801.         if (is_null($rownum)) {
  802.             $rownum = $this->highest_fetched_row[$result_value] + 1;
  803.         }
  804.         if (!isset($this->results[$result_value][$rownum])) {
  805.             if (isset($this->limits[$result_value])) {
  806.                 if ($rownum >= $this->limits[$result_value][1]) {
  807.                     return null;
  808.                 }
  809.                 if (MDB::isError($this->_skipLimitOffset($result))) {
  810.                     return null;
  811.                 }
  812.             }
  813.             if (isset($this->row_buffer[$result_value])) {
  814.                 $this->current_row[$result_value]++;
  815.                 $this->results[$result_value][$this->current_row[$result_value]] =
  816.                     $this->row_buffer[$result_value];
  817.                 unset($this->row_buffer[$result_value]);
  818.             }
  819.             while ($this->current_row[$result_value] < $rownum
  820.                 && is_array($buffer = @ibase_fetch_assoc($result))
  821.             ) {
  822.                 $this->current_row[$result_value]++;
  823.                 $this->results[$result_value][$this->current_row[$result_value]] = $buffer;
  824.             }
  825.             $this->highest_fetched_row[$result_value] =
  826.                 max($this->highest_fetched_row[$result_value],
  827.                     $this->current_row[$result_value]);
  828.         }
  829.         if (isset($this->results[$result_value][$rownum])) {
  830.             $this->highest_fetched_row[$result_value] =
  831.                 max($this->highest_fetched_row[$result_value], $rownum);
  832.             $row = $this->results[$result_value][$rownum];
  833.         } else {
  834.             return null;
  835.         }
  836.         foreach ($row as $key => $value_with_space) {
  837.             $row[$key] = rtrim($value_with_space, ' ');
  838.         }
  839.         if ($fetchmode == MDB_FETCHMODE_ASSOC) {
  840.             if ($this->options['optimize'] == 'portability') {
  841.                 $row = array_change_key_case($row, CASE_LOWER);
  842.             }
  843.         } else {
  844.             $row = array_values($row);
  845.         }
  846.         if (!$row) {
  847.             if($this->options['autofree']) {
  848.                 $this->freeResult($result);
  849.             }
  850.             return(NULL);
  851.         }
  852.         if (isset($this->result_types[$result])) {
  853.             $row = $this->convertResultRow($result, $row);
  854.         }
  855.         return $row;
  856.     }
  857.  
  858.     // }}}
  859.     // {{{ _retrieveLob()
  860.  
  861.     /**
  862.      * fetch a lob value from a result set
  863.      *
  864.      * @param int $lob handle to a lob created by the createLob() function
  865.      * @return mixed MDB_OK on success, a MDB error on failure
  866.      * @access private
  867.      */
  868.     function _retrieveLob($lob)
  869.     {
  870.         if (!isset($this->lobs[$lob])) {
  871.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  872.                 'Retrieve LOB: it was not specified a valid lob'));
  873.         }
  874.  
  875.         if (!isset($this->lobs[$lob]['Value'])) {
  876.             $this->lobs[$lob]['Value'] = $this->fetch($this->lobs[$lob]['Result'],
  877.                                                       $this->lobs[$lob]['Row'],
  878.                                                       $this->lobs[$lob]['Field']);
  879.  
  880.             if (!$this->lobs[$lob]['Handle'] = ibase_blob_open($this->lobs[$lob]['Value'])) {
  881.                 unset($this->lobs[$lob]['Value']);
  882.                 return($this->raiseError(MDB_ERROR, NULL, NULL,
  883.                     'Retrieve LOB: Could not open fetched large object field' . ibase_errmsg()));
  884.             }
  885.         }
  886.         return MDB_OK;
  887.     }
  888.  
  889.     // }}}
  890.     // {{{ endOfResultLob()
  891.  
  892.     /**
  893.      * Determine whether it was reached the end of the large object and
  894.      * therefore there is no more data to be read for the its input stream.
  895.      *
  896.      * @param int    $lob handle to a lob created by the createLob() function
  897.      * @return mixed TRUE or FALSE on success, a MDB error on failure
  898.      * @access public
  899.      */
  900.     function endOfResultLob($lob)
  901.     {
  902.         if (MDB::isError($lobresult = $this->_retrieveLob($lob))) {
  903.             return($lobresult);
  904.         }
  905.         return(isset($this->lobs[$lob]['EndOfLOB']));
  906.     }
  907.  
  908.     // }}}
  909.     // {{{ _readResultLob()
  910.  
  911.     /**
  912.      * Read data from large object input stream.
  913.      *
  914.      * @param int $lob handle to a lob created by the createLob() function
  915.      * @param blob $data reference to a variable that will hold data to be
  916.      *      read from the large object input stream
  917.      * @param int $length integer value that indicates the largest ammount of
  918.      *      data to be read from the large object input stream.
  919.      * @return mixed length on success, a MDB error on failure
  920.      * @access private
  921.      */
  922.     function _readResultLob($lob, &$data, $length)
  923.     {
  924.         if (MDB::isError($lobresult = $this->_retrieveLob($lob))) {
  925.             return $lobresult;
  926.         }
  927.         $data = ibase_blob_get($this->lobs[$lob]['Handle'], $length);
  928.         if (!is_string($data)) {
  929.             $this->raiseError(MDB_ERROR, NULL, NULL, 'Read Result LOB: ' . ibase_errmsg());
  930.         }
  931.         if (($length = strlen($data)) == 0) {
  932.             $this->lobs[$lob]['EndOfLOB'] = 1;
  933.         }
  934.         return $length;
  935.     }
  936.  
  937.     // }}}
  938.     // {{{ _destroyResultLob()
  939.  
  940.     /**
  941.      * Free any resources allocated during the lifetime of the large object
  942.      * handler object.
  943.      *
  944.      * @param int $lob handle to a lob created by the createLob() function
  945.      * @access private
  946.      */
  947.     function _destroyResultLob($lob)
  948.     {
  949.         if (isset($this->lobs[$lob])) {
  950.             if (isset($this->lobs[$lob]['Value'])) {
  951.                ibase_blob_close($this->lobs[$lob]['Handle']);
  952.             }
  953.             $this->lobs[$lob] = '';
  954.         }
  955.     }
  956.  
  957.     // }}}
  958.     // {{{ fetchClob()
  959.  
  960.     /**
  961.      * fetch a clob value from a result set
  962.      *
  963.      * @param resource $result result identifier
  964.      * @param int $row number of the row where the data can be found
  965.      * @param int $field field number where the data can be found
  966.      * @return mixed content of the specified data cell, a MDB error on failure,
  967.      *       a MDB error on failure
  968.      * @access public
  969.      */
  970.     function fetchClob($result, $row, $field)
  971.     {
  972.         return $this->fetchLob($result, $row, $field);
  973.     }
  974.  
  975.     // }}}
  976.     // {{{ fetchBlob()
  977.  
  978.     /**
  979.      * fetch a blob value from a result set
  980.      *
  981.      * @param resource $result result identifier
  982.      * @param int $row number of the row where the data can be found
  983.      * @param int $field field number where the data can be found
  984.      * @return mixed content of the specified data cell, a MDB error on failure
  985.      * @access public
  986.      */
  987.     function fetchBlob($result, $row, $field)
  988.     {
  989.         return $this->fetchLob($result, $row, $field);
  990.     }
  991.  
  992.     // }}}
  993.     // {{{ convertResult()
  994.  
  995.     /**
  996.      * convert a value to a RDBMS indepdenant MDB type
  997.      *
  998.      * @param mixed $value value to be converted
  999.      * @param int $type constant that specifies which type to convert to
  1000.      * @return mixed converted value or a MDB error on failure
  1001.      * @access public
  1002.      */
  1003.     function convertResult($value, $type)
  1004.     {
  1005.         switch ($type) {
  1006.             case MDB_TYPE_DECIMAL:
  1007.                 return sprintf('%.'.$this->decimal_places.'f', doubleval($value)/$this->decimal_factor);
  1008.             case MDB_TYPE_TIMESTAMP:
  1009.                 return substr($value, 0, strlen('YYYY-MM-DD HH:MM:SS'));
  1010.             default:
  1011.                 return $this->_baseConvertResult($value, $type);
  1012.         }
  1013.     }
  1014.  
  1015.     // }}}
  1016.     // {{{ resultIsNull()
  1017.  
  1018.     /**
  1019.      * Determine whether the value of a query result located in given row and
  1020.      *    field is a NULL.
  1021.      *
  1022.      * @param resource $result result identifier
  1023.      * @param int $rownum number of the row where the data can be found
  1024.      * @param int $field field number where the data can be found
  1025.      * @return mixed TRUE or FALSE on success, a MDB error on failure
  1026.      * @access public
  1027.      */
  1028.     function resultIsNull($result, $rownum, $field)
  1029.     {
  1030.         $value = $this->fetch($result, $rownum, $field);
  1031.         if (MDB::isError($value)) {
  1032.             return $value;
  1033.         }
  1034.         return(!isset($value));
  1035.     }
  1036.  
  1037.     // }}}
  1038.     // {{{ numRows()
  1039.  
  1040.     /**
  1041.      * returns the number of rows in a result object
  1042.      *
  1043.      * @param ressource $result a valid result ressouce pointer
  1044.      * @return mixed MDB_Error or the number of rows
  1045.      * @access public
  1046.      */
  1047.     function numRows($result)
  1048.     {
  1049.         $result_value = intval($result);
  1050.         if (!isset($this->current_row[$result_value])) {
  1051.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  1052.                 'Number of rows: attemped to obtain the number of rows contained in an unknown query result'));
  1053.         }
  1054.         if (!isset($this->rows[$result_value])) {
  1055.             if (MDB::isError($getcolumnnames = $this->getColumnNames($result))) {
  1056.                 return($getcolumnnames);
  1057.             }
  1058.             if (isset($this->limits[$result_value])) {
  1059.                 if (MDB::isError($skipfirstrow = $this->_skipLimitOffset($result))) {
  1060.                     $this->rows[$result_value] = 0;
  1061.                     return $skipfirstrow;
  1062.                 }
  1063.                 $limit = $this->limits[$result_value][1];
  1064.             } else {
  1065.                 $limit = 0;
  1066.             }
  1067.             if ($limit == 0 || $this->current_row[$result_value] + 1 < $limit) {
  1068.                 if (isset($this->row_buffer[$result_value])) {
  1069.                     $this->current_row[$result_value]++;
  1070.                     $this->results[$result_value][$this->current_row[$result_value]] = $this->row_buffer[$result_value];
  1071.                     unset($this->row_buffer[$result_value]);
  1072.                 }
  1073.                 while(($limit == 0 || $this->current_row[$result_value] + 1 < $limit)
  1074.                     && (is_array($row = @ibase_fetch_assoc($result)))
  1075.                 ) {
  1076.                     $this->current_row[$result_value]++;
  1077.                     $this->results[$result_value][$this->current_row[$result_value]] = $row;
  1078.                 }
  1079.             }
  1080.             $this->rows[$result_value] = $this->current_row[$result_value] + 1;
  1081.         }
  1082.         return $this->rows[$result_value];
  1083.     }
  1084.  
  1085.     // }}}
  1086.     // {{{ freeResult()
  1087.  
  1088.     /**
  1089.      * Free the internal resources associated with $result.
  1090.      *
  1091.      * @param $result result identifier
  1092.      * @return boolean TRUE on success, FALSE if $result is invalid
  1093.      * @access public
  1094.      */
  1095.     function freeResult($result)
  1096.     {
  1097.         $result_value = intval($result);
  1098.         if (!isset($this->current_row[$result_value])) {
  1099.            return($this->raiseError(MDB_ERROR, NULL, NULL,
  1100.                'Free result: attemped to free an unknown query result'));
  1101.         }
  1102.         if (isset($this->highest_fetched_row[$result])) {
  1103.             unset($this->highest_fetched_row[$result]);
  1104.         }
  1105.         if (isset($this->row_buffer[$result_value])) {
  1106.             unset($this->row_buffer[$result_value]);
  1107.         }
  1108.         if (isset($this->limits[$result_value])) {
  1109.             unset($this->limits[$result_value]);
  1110.         }
  1111.         if (isset($this->current_row[$result_value])) {
  1112.             unset($this->current_row[$result_value]);
  1113.         }
  1114.         if (isset($this->results[$result_value])) {
  1115.             unset($this->results[$result_value]);
  1116.         }
  1117.         if (isset($this->columns[$result])) {
  1118.             unset($this->columns[$result]);
  1119.         }
  1120.         if (isset($this->rows[$result_value])) {
  1121.             unset($this->rows[$result_value]);
  1122.         }
  1123.         if (isset($this->result_types[$result])) {
  1124.             unset($this->result_types[$result]);
  1125.         }
  1126.         if (is_resource($result)) {
  1127.             return @ibase_free_result($result);
  1128.         }
  1129.         return true;
  1130.     }
  1131.     // }}}
  1132.     // {{{ getTypeDeclaration()
  1133.  
  1134.     /**
  1135.      * Obtain DBMS specific SQL code portion needed to declare an text type
  1136.      * field to be used in statements like CREATE TABLE.
  1137.      *
  1138.      * @param string $field  associative array with the name of the properties
  1139.      *      of the field being declared as array indexes. Currently, the types
  1140.      *      of supported field properties are as follows:
  1141.      *
  1142.      *      length
  1143.      *          Integer value that determines the maximum length of the text
  1144.      *          field. If this argument is missing the field should be
  1145.      *          declared to have the longest length allowed by the DBMS.
  1146.      *
  1147.      *      default
  1148.      *          Text value to be used as default for this field.
  1149.      *
  1150.      *      notnull
  1151.      *          Boolean flag that indicates whether this field is constrained
  1152.      *          to not be set to null.
  1153.      * @return string  DBMS specific SQL code portion that should be used to
  1154.      *      declare the specified field.
  1155.      * @access public
  1156.      */
  1157.     function getTypeDeclaration($field)
  1158.     {
  1159.         switch($field['type'])
  1160.         {
  1161.             case 'text':
  1162.                 return('VARCHAR ('.(isset($field['length']) ? $field['length'] : (isset($this->options['DefaultTextFieldLength']) ? $this->options['DefaultTextFieldLength'] : 4000)).')');
  1163.             case 'clob':
  1164.                 return 'BLOB SUB_TYPE 1';
  1165.             case 'blob':
  1166.                 return 'BLOB SUB_TYPE 0';
  1167.             case 'integer':
  1168.                 return 'INTEGER';
  1169.             case 'boolean':
  1170.                 return 'CHAR (1)';
  1171.             case 'date':
  1172.                 return 'DATE';
  1173.             case 'time':
  1174.                 return 'TIME';
  1175.             case 'timestamp':
  1176.                 return 'TIMESTAMP';
  1177.             case 'float':
  1178.                 return 'DOUBLE PRECISION';
  1179.             case 'decimal':
  1180.                 return 'DECIMAL(18,'.$this->decimal_places.')';
  1181.         }
  1182.         return '';
  1183.     }
  1184.  
  1185.     // }}}
  1186.     // {{{ getTextDeclaration()
  1187.  
  1188.     /**
  1189.      * Obtain DBMS specific SQL code portion needed to declare an text type
  1190.      * field to be used in statements like CREATE TABLE.
  1191.      *
  1192.      * @param string $name   name the field to be declared.
  1193.      * @param string $field  associative array with the name of the properties
  1194.      *      of the field being declared as array indexes. Currently, the types
  1195.      *      of supported field properties are as follows:
  1196.      *
  1197.      *      length
  1198.      *          Integer value that determines the maximum length of the text
  1199.      *          field. If this argument is missing the field should be
  1200.      *          declared to have the longest length allowed by the DBMS.
  1201.      *
  1202.      *      default
  1203.      *          Text value to be used as default for this field.
  1204.      *
  1205.      *      notnull
  1206.      *          Boolean flag that indicates whether this field is constrained
  1207.      *          to not be set to NULL.
  1208.      * @return string  DBMS specific SQL code portion that should be used to
  1209.      *      declare the specified field.
  1210.      * @access public
  1211.      */
  1212.     function getTextDeclaration($name, $field)
  1213.     {
  1214.         return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getTextValue($field['default']) : '').(IsSet($field['notnull']) ? ' NOT NULL' : ''));
  1215.     }
  1216.  
  1217.     // }}}
  1218.     // {{{ getClobDeclaration()
  1219.  
  1220.     /**
  1221.      * Obtain DBMS specific SQL code portion needed to declare an character
  1222.      * large object type field to be used in statements like CREATE TABLE.
  1223.      *
  1224.      * @param string $name   name the field to be declared.
  1225.      * @param string $field  associative array with the name of the properties
  1226.      *      of the field being declared as array indexes. Currently, the types
  1227.      *      of supported field properties are as follows:
  1228.      *
  1229.      *      length
  1230.      *          Integer value that determines the maximum length of the large
  1231.      *          object field. If this argument is missing the field should be
  1232.      *          declared to have the longest length allowed by the DBMS.
  1233.      *
  1234.      *      notnull
  1235.      *          Boolean flag that indicates whether this field is constrained
  1236.      *          to not be set to NULL.
  1237.      * @return string  DBMS specific SQL code portion that should be used to
  1238.      *      declare the specified field.
  1239.      * @access public
  1240.      */
  1241.     function getClobDeclaration($name, $field)
  1242.     {
  1243.         return($name.' '.$this->getTypeDeclaration($field).(isset($field['notnull']) ? ' NOT NULL' : ''));
  1244.     }
  1245.  
  1246.     // }}}
  1247.     // {{{ getBlobDeclaration()
  1248.  
  1249.     /**
  1250.      * Obtain DBMS specific SQL code portion needed to declare an binary large
  1251.      * object type field to be used in statements like CREATE TABLE.
  1252.      *
  1253.      * @param string $name   name the field to be declared.
  1254.      * @param string $field  associative array with the name of the properties
  1255.      *      of the field being declared as array indexes. Currently, the types
  1256.      *      of supported field properties are as follows:
  1257.      *
  1258.      *      length
  1259.      *          Integer value that determines the maximum length of the large
  1260.      *          object field. If this argument is missing the field should be
  1261.      *          declared to have the longest length allowed by the DBMS.
  1262.      *
  1263.      *      notnull
  1264.      *          Boolean flag that indicates whether this field is constrained
  1265.      *          to not be set to NULL.
  1266.      * @return string  DBMS specific SQL code portion that should be used to
  1267.      *      declare the specified field.
  1268.      * @access public
  1269.      */
  1270.     function getBlobDeclaration($name, $field)
  1271.     {
  1272.         return($name.' '.$this->getTypeDeclaration($field).(isset($field['notnull']) ? ' NOT NULL' : ''));
  1273.     }
  1274.  
  1275.     // }}}
  1276.     // {{{ getDateDeclaration()
  1277.  
  1278.     /**
  1279.      * Obtain DBMS specific SQL code portion needed to declare a date type
  1280.      * field to be used in statements like CREATE TABLE.
  1281.      *
  1282.      * @param string $name   name the field to be declared.
  1283.      * @param string $field  associative array with the name of the properties
  1284.      *      of the field being declared as array indexes. Currently, the types
  1285.      *      of supported field properties are as follows:
  1286.      *
  1287.      *      default
  1288.      *          Date value to be used as default for this field.
  1289.      *
  1290.      *      notnull
  1291.      *          Boolean flag that indicates whether this field is constrained
  1292.      *          to not be set to NULL.
  1293.      * @return string  DBMS specific SQL code portion that should be used to
  1294.      *      declare the specified field.
  1295.      * @access public
  1296.      */
  1297.     function getDateDeclaration($name, $field)
  1298.     {
  1299.         return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT "'.$field['default'].'"' : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1300.     }
  1301.  
  1302.     // }}}
  1303.     // {{{ getTimeDeclaration()
  1304.  
  1305.     /**
  1306.      * Obtain DBMS specific SQL code portion needed to declare a time
  1307.      * field to be used in statements like CREATE TABLE.
  1308.      *
  1309.      * @param string $name   name the field to be declared.
  1310.      * @param string $field  associative array with the name of the properties
  1311.      *      of the field being declared as array indexes. Currently, the types
  1312.      *      of supported field properties are as follows:
  1313.      *
  1314.      *      default
  1315.      *          Time value to be used as default for this field.
  1316.      *
  1317.      *      notnull
  1318.      *          Boolean flag that indicates whether this field is constrained
  1319.      *          to not be set to NULL.
  1320.      * @return string  DBMS specific SQL code portion that should be used to
  1321.      *      declare the specified field.
  1322.      * @access public
  1323.      */
  1324.     function getTimeDeclaration($name, $field)
  1325.     {
  1326.         return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT "'.$field['default'].'"' : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1327.     }
  1328.  
  1329.     // }}}
  1330.     // {{{ getFloatDeclaration()
  1331.  
  1332.     /**
  1333.      * Obtain DBMS specific SQL code portion needed to declare a float type
  1334.      * field to be used in statements like CREATE TABLE.
  1335.      *
  1336.      * @param string $name   name the field to be declared.
  1337.      * @param string $field  associative array with the name of the properties
  1338.      *      of the field being declared as array indexes. Currently, the types
  1339.      *      of supported field properties are as follows:
  1340.      *
  1341.      *      default
  1342.      *          Float value to be used as default for this field.
  1343.      *
  1344.      *      notnull
  1345.      *          Boolean flag that indicates whether this field is constrained
  1346.      *          to not be set to NULL.
  1347.      * @return string  DBMS specific SQL code portion that should be used to
  1348.      *      declare the specified field.
  1349.      * @access public
  1350.      */
  1351.     function getFloatDeclaration($name, $field)
  1352.     {
  1353.         return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getFloatValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1354.     }
  1355.  
  1356.     // }}}
  1357.     // {{{ getDecimalDeclaration()
  1358.  
  1359.     /**
  1360.      * Obtain DBMS specific SQL code portion needed to declare a decimal type
  1361.      * field to be used in statements like CREATE TABLE.
  1362.      *
  1363.      * @param string $name   name the field to be declared.
  1364.      * @param string $field  associative array with the name of the properties
  1365.      *      of the field being declared as array indexes. Currently, the types
  1366.      *      of supported field properties are as follows:
  1367.      *
  1368.      *      default
  1369.      *          Decimal value to be used as default for this field.
  1370.      *
  1371.      *      notnull
  1372.      *          Boolean flag that indicates whether this field is constrained
  1373.      *          to not be set to NULL.
  1374.      * @return string  DBMS specific SQL code portion that should be used to
  1375.      *      declare the specified field.
  1376.      * @access public
  1377.      */
  1378.     function getDecimalDeclaration($name, $field)
  1379.     {
  1380.         return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getDecimalValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1381.     }
  1382.  
  1383.     // }}}
  1384.     // {{{ _getLobValue()
  1385.  
  1386.     /**
  1387.      * Convert a text value into a DBMS specific format that is suitable to
  1388.      * compose query statements.
  1389.      *
  1390.      * @param resource  $prepared_query query handle from prepare()
  1391.      * @param           $parameter
  1392.      * @param           $lob
  1393.      * @return string text string that represents the given argument value in
  1394.      *      a DBMS specific format.
  1395.      * @access private
  1396.      */
  1397.     function _getLobValue($prepared_query, $parameter, $lob)
  1398.     {
  1399.         if (MDB::isError($connect = $this->connect())) {
  1400.             return $connect;
  1401.         }
  1402.         $value   = '';  // DEAL WITH ME
  1403.         if (!$this->transaction_id = ibase_trans(IBASE_COMMITTED, $this->connection)) {
  1404.             return($this->raiseError(MDB_ERROR, '', '', '_getLobValue: Could not start a new transaction: '.ibase_errmsg()));
  1405.         }
  1406.  
  1407.         if (($lo = ibase_blob_create($this->auto_commit ? $this->connection : $this->transaction_id))) {
  1408.             while (!$this->endOfLob($lob)) {
  1409.                 if (MDB::isError($result = $this->readLob($lob, $data, $this->options['lob_buffer_length']))) {
  1410.                     break;
  1411.                 }
  1412.                 if (ibase_blob_add($lo, $data) === false) {
  1413.                     $result = $this->raiseError(MDB_ERROR, NULL, NULL, '_getLobValue - Could not add data to a large object: ' . ibase_errmsg());
  1414.                     break;
  1415.                 }
  1416.             }
  1417.             if (MDB::isError($result)) {
  1418.                 ibase_blob_cancel($lo);
  1419.             } else {
  1420.                 $value = ibase_blob_close($lo);
  1421.             }
  1422.         } else {
  1423.             $result = $this->raiseError(MDB_ERROR, NULL, NULL, 'Get LOB field value: ' . pg_ErrorMessage($this->connection));
  1424.         }
  1425.         if (!isset($this->query_parameters[$prepared_query])) {
  1426.             $this->query_parameters[$prepared_query]       = array(0, '');
  1427.             $this->query_parameter_values[$prepared_query] = array();
  1428.         }
  1429.         $query_parameter = count($this->query_parameters[$prepared_query]);
  1430.         $this->query_parameter_values[$prepared_query][$parameter] = $query_parameter;
  1431.         $this->query_parameters[$prepared_query][$query_parameter] = $value;
  1432.         $value = '?';
  1433.  
  1434.         if (!$this->auto_commit) {
  1435.             $this->commit();
  1436.         }
  1437.         return $value;
  1438.     }
  1439.  
  1440.     // }}}
  1441.     // {{{ getClobValue()
  1442.  
  1443.     /**
  1444.      * Convert a text value into a DBMS specific format that is suitable to
  1445.      * compose query statements.
  1446.      *
  1447.      * @param resource  $prepared_query query handle from prepare()
  1448.      * @param           $parameter
  1449.      * @param           $clob
  1450.      * @return string text string that represents the given argument value in
  1451.      *      a DBMS specific format.
  1452.      * @access public
  1453.      */
  1454.     function getClobValue($prepared_query, $parameter, $clob)
  1455.     {
  1456.         return $this->_getLobValue($prepared_query, $parameter, $clob);
  1457.     }
  1458.  
  1459.  
  1460.     // }}}
  1461.     // {{{ freeLobValue()
  1462.  
  1463.     /**
  1464.      * free a large object
  1465.      *
  1466.      * @param resource  $prepared_query query handle from prepare()
  1467.      * @param string    $lob
  1468.      * @param string    $value
  1469.      * @return MDB_OK
  1470.      * @access public
  1471.      */
  1472.     function freeLobValue($prepared_query, $lob, &$value)
  1473.     {
  1474.         $query_parameter=$this->query_parameter_values[$prepared_query][$lob];
  1475.  
  1476.         unset($this->query_parameters[$prepared_query][$query_parameter]);
  1477.         unset($this->query_parameter_values[$prepared_query][$lob]);
  1478.         if (count($this->query_parameter_values[$prepared_query]) == 0) {
  1479.             unset($this->query_parameters[$prepared_query]);
  1480.             unset($this->query_parameter_values[$prepared_query]);
  1481.         }
  1482.         unset($value);
  1483.     }
  1484.  
  1485.     // }}}
  1486.     // {{{ freeClobValue()
  1487.  
  1488.     /**
  1489.      * free a character large object
  1490.      *
  1491.      * @param resource  $prepared_query query handle from prepare()
  1492.      * @param string    $clob
  1493.      * @param string    $value
  1494.      * @return MDB_OK
  1495.      * @access public
  1496.      */
  1497.     function freeClobValue($prepared_query, $clob, &$value)
  1498.     {
  1499.         $this->freeLobValue($prepared_query, $clob, $value);
  1500.     }
  1501.  
  1502.     // }}}
  1503.     // {{{ getBlobValue()
  1504.  
  1505.     /**
  1506.      * Convert a text value into a DBMS specific format that is suitable to
  1507.      * compose query statements.
  1508.      *
  1509.      * @param resource  $prepared_query query handle from prepare()
  1510.      * @param           $parameter
  1511.      * @param           $blob
  1512.      * @return string text string that represents the given argument value in
  1513.      *      a DBMS specific format.
  1514.      * @access public
  1515.      */
  1516.     function getBlobValue($prepared_query, $parameter, $blob)
  1517.     {
  1518.         return $this->_getLobValue($prepared_query, $parameter, $blob);
  1519.     }
  1520.  
  1521.     // }}}
  1522.     // {{{ freeBlobValue()
  1523.  
  1524.     /**
  1525.      * free a binary large object
  1526.      *
  1527.      * @param resource  $prepared_query query handle from prepare()
  1528.      * @param string    $blob
  1529.      * @param string    $value
  1530.      * @return MDB_OK
  1531.      * @access public
  1532.      */
  1533.     function freeBlobValue($prepared_query, $blob, &$value)
  1534.     {
  1535.         $this->freeLobValue($prepared_query, $blob, $value);
  1536.     }
  1537.  
  1538.     // }}}
  1539.     // {{{ getFloatValue()
  1540.  
  1541.     /**
  1542.      * Convert a text value into a DBMS specific format that is suitable to
  1543.      * compose query statements.
  1544.      *
  1545.      * @param string $value text string value that is intended to be converted.
  1546.      * @return string text string that represents the given argument value in
  1547.      *      a DBMS specific format.
  1548.      * @access public
  1549.      */
  1550.     function getFloatValue($value)
  1551.     {
  1552.         return (($value === NULL) ? 'NULL' : $value);
  1553.     }
  1554.  
  1555.     // }}}
  1556.     // {{{ getDecimalValue()
  1557.  
  1558.     /**
  1559.      * Convert a text value into a DBMS specific format that is suitable to
  1560.      * compose query statements.
  1561.      *
  1562.      * @param string $value text string value that is intended to be converted.
  1563.      * @return string text string that represents the given argument value in
  1564.      *      a DBMS specific format.
  1565.      * @access public
  1566.      */
  1567.     function getDecimalValue($value)
  1568.     {
  1569.         return (($value === null) ? 'NULL' : strval(round($value*$this->decimal_factor)));
  1570.     }
  1571.  
  1572.     // }}}
  1573.     // {{{ nextId()
  1574.  
  1575.     /**
  1576.      * returns the next free id of a sequence
  1577.      *
  1578.      * @param string  $seq_name name of the sequence
  1579.      * @param boolean $ondemand when TRUE the seqence is
  1580.      *                          automatic created, if it
  1581.      *                          not exists
  1582.      * @return mixed MDB_Error or id
  1583.      * @access public
  1584.      */
  1585.     function nextId($seq_name, $ondemand = true)
  1586.     {
  1587.         if (MDB::isError($connect = $this->connect())) {
  1588.             return $connect;
  1589.         }
  1590.         $sequence_name = $this->getSequenceName($seq_name);
  1591.         $this->expectError(MDB_ERROR_NOSUCHTABLE);
  1592.         $result = $this->_doQuery("SELECT GEN_ID($sequence_name, 1) as the_value FROM RDB\$DATABASE");
  1593.         $this->popExpect();
  1594.         if ($ondemand && MDB::isError($result)
  1595.             //&& $result->getCode() == MDB_ERROR_NOSUCHTABLE
  1596.             )
  1597.         {
  1598.             // Since we are create the sequence on demand
  1599.             // we know the first id = 1 so initialize the
  1600.             // sequence at 2
  1601.             $result = $this->createSequence($seq_name, 2);
  1602.             if (MDB::isError($result)) {
  1603.                 return($this->raiseError(MDB_ERROR, NULL, NULL,
  1604.                     'Next ID: on demand sequence could not be created'));
  1605.             } else {
  1606.                 // First ID of a newly created sequence is 1
  1607.                 return 1;
  1608.             }
  1609.         }
  1610.         return $this->fetchOne($result);
  1611.     }
  1612.  
  1613.     // }}}
  1614.     // {{{ currId()
  1615.  
  1616.     /**
  1617.      * returns the current id of a sequence
  1618.      *
  1619.      * @param string  $seq_name name of the sequence
  1620.      * @return mixed MDB_Error or id
  1621.      * @access public
  1622.      */
  1623.     function currId($seq_name)
  1624.     {
  1625.         $seqname = $this->getSequenceName($seq_name);
  1626.         if (MDB::isError($result = $this->queryOne("SELECT RDB\$GENERATOR_ID FROM RDB\$GENERATORS WHERE RDB\$GENERATOR_NAME='$seqname'"))) {
  1627.             return($this->raiseError(MDB_ERROR, NULL, NULL, 'currId: Unable to select from ' . $seqname) );
  1628.         }
  1629.         if (!is_numeric($result)) {
  1630.             return($this->raiseError(MDB_ERROR, NULL, NULL, 'currId: could not find value in sequence table'));
  1631.         }
  1632.         return $result;
  1633.     }
  1634.  
  1635.     // }}}
  1636.     // {{{ nextResult()
  1637.  
  1638.     /**
  1639.      * Move the internal ibase result pointer to the next available result
  1640.      *
  1641.      * @param $result a valid ibase result resource
  1642.      * @return TRUE if a result is available otherwise return FALSE
  1643.      * @access public
  1644.      */
  1645.     function nextResult($result)
  1646.     {
  1647.         return false;
  1648.     }
  1649.  
  1650.     // }}}
  1651.     // {{{ tableInfo()
  1652.  
  1653.     /**
  1654.      * returns meta data about the result set
  1655.      *
  1656.      * @param  mixed $resource FireBird/InterBase result identifier or table name
  1657.      * @param mixed $mode depends on implementation
  1658.      * @return array an nested array, or a MDB error
  1659.      * @access public
  1660.      */
  1661.     function tableInfo($result, $mode = NULL)
  1662.     {
  1663.         $count = 0;
  1664.         $id = 0;
  1665.         $res = array();
  1666.  
  1667.         /**
  1668.          * depending on $mode, metadata returns the following values:
  1669.          *
  1670.          * - mode is FALSE (default):
  1671.          * $result[]:
  1672.          *    [0]['table']  table name
  1673.          *    [0]['name']   field name
  1674.          *    [0]['type']   field type
  1675.          *    [0]['len']    field length
  1676.          *    [0]['flags']  field flags
  1677.          *
  1678.          * - mode is MDB_TABLEINFO_ORDER
  1679.          * $result[]:
  1680.          *    ['num_fields'] number of metadata records
  1681.          *    [0]['table']  table name
  1682.          *    [0]['name']   field name
  1683.          *    [0]['type']   field type
  1684.          *    [0]['len']    field length
  1685.          *    [0]['flags']  field flags
  1686.          *    ['order'][field name]  index of field named 'field name'
  1687.          *    The last one is used, if you have a field name, but no index.
  1688.          *    Test:  if (isset($result['meta']['myfield'])) { ...
  1689.          *
  1690.          * - mode is MDB_TABLEINFO_ORDERTABLE
  1691.          *     the same as above. but additionally
  1692.          *    ['ordertable'][table name][field name] index of field
  1693.          *       named 'field name'
  1694.          *
  1695.          *       this is, because if you have fields from different
  1696.          *       tables with the same field name * they override each
  1697.          *       other with MDB_TABLEINFO_ORDER
  1698.          *
  1699.          *       you can combine MDB_TABLEINFO_ORDER and
  1700.          *       MDB_TABLEINFO_ORDERTABLE with MDB_TABLEINFO_ORDER |
  1701.          *       MDB_TABLEINFO_ORDERTABLE * or with MDB_TABLEINFO_FULL
  1702.          **/
  1703.  
  1704.         // if $result is a string, then we want information about a
  1705.         // table without a resultset
  1706.         if (is_string($result)) {
  1707.             $id = ibase_query($this->connection,"SELECT * FROM $result");
  1708.             if (empty($id)) {
  1709.                 return $this->ibaseRaiseError();
  1710.             }
  1711.         } else { // else we want information about a resultset
  1712.             $id = $result;
  1713.             if (empty($id)) {
  1714.                 return $this->ibaseRaiseError();
  1715.             }
  1716.         }
  1717.  
  1718.         $count = @ibase_num_fields($id);
  1719.  
  1720.         // made this IF due to performance (one if is faster than $count if's)
  1721.         if (empty($mode)) {
  1722.             for ($i=0; $i<$count; $i++) {
  1723.                 $info = @ibase_field_info($id, $i);
  1724.                 //$res[$i]['table'] = (is_string($result)) ? $result : '';
  1725.                 $res[$i]['table'] = (is_string($result)) ? $result : $info['relation'];
  1726.                 $res[$i]['name']  = $info['name'];
  1727.                 $res[$i]['type']  = $info['type'];
  1728.                 $res[$i]['len']   = $info['length'];
  1729.                 //$res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($info['name'], $result) : '';
  1730.                 $res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($id, $i, $result) : '';
  1731.             }
  1732.         } else { // full
  1733.             $res['num_fields'] = $count;
  1734.  
  1735.             for ($i=0; $i<$count; $i++) {
  1736.                 $info = @ibase_field_info($id, $i);
  1737.                 //$res[$i]['table'] = (is_string($result)) ? $result : '';
  1738.                 $res[$i]['table'] = (is_string($result)) ? $result : $info['relation'];
  1739.                 $res[$i]['name']  = $info['name'];
  1740.                 $res[$i]['type']  = $info['type'];
  1741.                 $res[$i]['len']   = $info['length'];
  1742.                 //$res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($info['name'], $result) : '';
  1743.                 $res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($id, $i, $result) : '';
  1744.                 if ($mode & MDB_TABLEINFO_ORDER) {
  1745.                     $res['order'][$res[$i]['name']] = $i;
  1746.                 }
  1747.                 if ($mode & MDB_TABLEINFO_ORDERTABLE) {
  1748.                     $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  1749.                 }
  1750.             }
  1751.         }
  1752.  
  1753.         // free the result only if we were called on a table
  1754.         if (is_string($result) && is_resource($id)) {
  1755.             @ibase_free_result($id);
  1756.         }
  1757.         return $res;
  1758.     }
  1759.  
  1760.     // }}}
  1761.     // {{{ _ibaseFieldFlags()
  1762.  
  1763.     /**
  1764.      * get the Flags of a Field
  1765.      *
  1766.      * @param int $resource FireBird/InterBase result identifier
  1767.      * @param int $num_field the field number
  1768.      * @return string The flags of the field ('not_null', 'default_xx', 'primary_key',
  1769.      *                 'unique' and 'multiple_key' are supported)
  1770.      * @access private
  1771.      **/
  1772.     function _ibaseFieldFlags($resource, $num_field, $table_name)
  1773.     {
  1774.         $field_name = @ibase_field_info($resource, $num_field);
  1775.         $field_name = @$field_name['name'];
  1776.         $sql = 'SELECT  R.RDB$CONSTRAINT_TYPE CTYPE'
  1777.                .' FROM  RDB$INDEX_SEGMENTS I'
  1778.                .' JOIN  RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
  1779.               .' WHERE  I.RDB$FIELD_NAME=\''.$field_name.'\''
  1780.                  .' AND R.RDB$RELATION_NAME=\''.$table_name.'\'';
  1781.         $result = ibase_query($this->connection, $sql);
  1782.         if (empty($result)) {
  1783.             return $this->ibaseRaiseError();
  1784.         }
  1785.         $flags = '';
  1786.         if ($obj = @ibase_fetch_object($result)) {
  1787.             ibase_free_result($result);
  1788.             if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'PRIMARY KEY') {
  1789.                 $flags = 'primary_key ';
  1790.             }
  1791.             if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'UNIQUE') {
  1792.                 $flags .= 'unique_key ';
  1793.             }
  1794.         }
  1795.  
  1796.         $sql = 'SELECT  R.RDB$NULL_FLAG AS NFLAG,'
  1797.                      .' R.RDB$DEFAULT_SOURCE AS DSOURCE,'
  1798.                      .' F.RDB$FIELD_TYPE AS FTYPE,'
  1799.                      .' F.RDB$COMPUTED_SOURCE AS CSOURCE'
  1800.                .' FROM  RDB$RELATION_FIELDS R '
  1801.                .' JOIN  RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
  1802.               .' WHERE  R.RDB$RELATION_NAME=\''.$table_name.'\''
  1803.                 .' AND  R.RDB$FIELD_NAME=\''.$field_name.'\'';
  1804.         $result = ibase_query($this->connection, $sql);
  1805.         if (empty($result)) {
  1806.             return $this->ibaseRaiseError();
  1807.         }
  1808.         if ($obj = @ibase_fetch_object($result)) {
  1809.             ibase_free_result($result);
  1810.             if (isset($obj->NFLAG)) {
  1811.                 $flags .= 'not_null ';
  1812.             }
  1813.             if (isset($obj->DSOURCE)) {
  1814.                 $flags .= 'default ';
  1815.             }
  1816.             if (isset($obj->CSOURCE)) {
  1817.                 $flags .= 'computed ';
  1818.             }
  1819.             if (isset($obj->FTYPE)  && $obj->FTYPE == 261) {
  1820.                 $flags .= 'blob ';
  1821.             }
  1822.         }
  1823.  
  1824.          return trim($flags);
  1825.     }
  1826. }
  1827.  
  1828. ?>